/*
 * Copyright (c) 2006-2018, Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-01-06     lfz    first version
 */
#include <rtthread.h>

#ifdef RT_USING_PM
#include "pm.h"
#include <drv_pm.h>
#include "bsp_lptime.h"
#include "bsp_system.h"
#include "drv_uart.h"

extern uint32_t SystemCoreClock;

/* 低功耗定时器中断回调函数 */
static void drv_pm_lptim_callback(void)
{
	/* enter interrupt */
	rt_interrupt_enter();

	/* leave interrupt */
	rt_interrupt_leave();
}

/* bsp初始化函数 */
static void pm_bsp_init(void)
{
	/* 重新配置debug串口 */
	finsh_uart_reconfig();
}

/* bsp去初始化函数 */
static void pm_bsp_deinit(void)
{
	// TODO XXXX
}

/* 初始化IO配置 */
static void pm_low_power_mode_gpio_set(void)
{
 
}

/**
 * @brief This function will put STM32L4xx into sleep mode.
 *
 * @param pm pointer to power manage structure
 */
static void pm_sleep(struct rt_pm *pm, rt_uint8_t mode)
{
	switch (mode) {		
	/* 运行模式:
	 * 未采取任何的降低功耗状态
	 */
	case PM_SLEEP_MODE_NONE:				
	break;

	/* 空闲模式:
	 * 在系统空闲时停止CPU和部分时钟,任意事件或中断均可以唤醒	
	 */
	case PM_SLEEP_MODE_IDLE:				
			__WFI();
	break;

	/* 轻度睡眠模式:
	 * CPU停止运行,但时钟模块、系统时钟、
	 * NVIC中断处理以及周边的功能模块仍都可以工作
	 */
	case PM_SLEEP_MODE_LIGHT:
	{     		
			pm_bsp_deinit();														/* 1.外设去初始化 */
			pm_low_power_mode_gpio_set();								/* 2.初始化IO配置 */
//			Lpm_GotoSleep(FALSE);												/* 3.进入普通睡眠模式*/
	}
	break;

	/*	深度睡眠模式:
	 *	CPU停止运行,高速时钟关闭,
	 *  低速时钟可配置是否运行,部分低功耗的周边模块可配置是否允许,
	 *  NVIC中断处理仍可以工作
	 */
	case PM_SLEEP_MODE_DEEP:			
	{	  
			pm_bsp_deinit();														/* 1.外设去去初始化 */	
			pm_low_power_mode_gpio_set();								/* 2.初始化IO配置 */
//			Lpm_GotoDeepSleep(FALSE);										/* 3.进入深度睡眠模式*/		
	}
	break;

	/*	待机模式:
	 *	CPU停止,设备上下文丢失(可保存至特殊外设),唤醒后复位
	 */
	case PM_SLEEP_MODE_STANDBY:
	break;

	/*	关断模式
	 *	比Standby模式功耗更低,上下文通常不可恢复,唤醒后复位
	 */
	case PM_SLEEP_MODE_SHUTDOWN:
	break;

	default:
			RT_ASSERT(0);
	break;
	}
}

static void pm_run(struct rt_pm *pm, rt_uint8_t mode)
{
	static uint8_t last_mode = RT_PM_DEFAULT_RUN_MODE;
	static char *run_str[]   = PM_RUN_MODE_NAMES;

	if (mode == last_mode){
			return;
	}      
	last_mode = mode;

	/* 1. 根据RUN模式切换时钟频率 */
	switch (mode) {
	case PM_RUN_MODE_HIGH_SPEED:				//设置为24MHz
//			 sysctrl_rch_set(SysctrlRchFreq24MHz);
	break;
	
	case PM_RUN_MODE_NORMAL_SPEED:			//设置为16MHz
//			 sysctrl_rch_set(SysctrlRchFreq16MHz);
	break;
	
	case PM_RUN_MODE_MEDIUM_SPEED:			//设置为8MHz
//			 sysctrl_rch_set(SysctrlRchFreq8MHz);
	break;
	
	case PM_RUN_MODE_LOW_SPEED:					//设置为4MHz
//			 sysctrl_rch_set(SysctrlRchFreq4MHz);
	break;
	
	default:
	break;
	}
	
	/* 2. 重新配置systick时钟 */
	systick_config(SystemCoreClock / RT_TICK_PER_SECOND);
	
	/* 3. 外设初始化和配置 */
	pm_bsp_init();
	
	rt_kprintf("switch to %s mode, frequency = %d MHz\n", run_str[mode], SystemCoreClock / 1000000);
}

/**
 * @brief This function caculate the PM tick from OS tick
 *
 * @param tick OS tick
 *
 * @return the PM tick
 */
static rt_tick_t pm_tick_from_os_tick(rt_tick_t tick)
{
	rt_uint32_t freq = bsp_lptim_get_countfreq(e_bsp_lptim1);

	return (freq * tick / RT_TICK_PER_SECOND);
}

/**
 * @brief This function caculate the OS tick from PM tick
 *
 * @param tick PM tick
 *
 * @return the OS tick
 */
static rt_tick_t os_tick_from_pm_tick(rt_uint32_t tick)
{
	static rt_uint32_t os_tick_remain = 0;
	rt_uint32_t ret, freq;

	freq = bsp_lptim_get_countfreq(e_bsp_lptim1);
	ret = (tick * RT_TICK_PER_SECOND + os_tick_remain) / freq;

	os_tick_remain += (tick * RT_TICK_PER_SECOND);
	os_tick_remain %= freq;

	return ret;
}

/**
 * @brief This function start the timer of pm
 *
 * @param pm Pointer to power manage structure
 * @param timeout How many OS Ticks that MCU can sleep
 */
static void pm_timer_start(struct rt_pm *pm, rt_uint32_t timeout)
{
	RT_ASSERT(pm != RT_NULL);
	RT_ASSERT(timeout > 0);

	if (timeout != RT_TICK_MAX) {
			/* Convert OS Tick to pmtimer timeout value */
			timeout = pm_tick_from_os_tick(timeout);
			if (timeout > bsp_lptim_get_tick_max()) {
					timeout = bsp_lptim_get_tick_max();
			}

			/* Enter PM_TIMER_MODE */
			bsp_lptime_start(e_bsp_lptim1, timeout);
	}
}

/**
 * @brief This function stop the timer of pm
 *
 * @param pm Pointer to power manage structure
 */
static void pm_timer_stop(struct rt_pm *pm)
{
	RT_ASSERT(pm != RT_NULL);

	/* Reset pmtimer status */
	bsp_lptime_stop(e_bsp_lptim1);
}

/**
 * @brief This function calculate how many OS Ticks that MCU have suspended
 *
 * @param pm Pointer to power manage structure
 *
 * @return OS Ticks
 */
static rt_tick_t pm_timer_get_tick(struct rt_pm *pm)
{
	rt_uint32_t timer_tick;

	RT_ASSERT(pm != RT_NULL);

	timer_tick = bsp_lptim_get_current_tick(e_bsp_lptim1);

	return os_tick_from_pm_tick(timer_tick);
}

static const struct rt_pm_ops _ops =
{
	pm_sleep,
	pm_run,
	pm_timer_start,
	pm_timer_stop,
	pm_timer_get_tick
};

/**
 * @brief This function initialize the power manager
 */
int32_t drv_pm_hw_init(void)
{
	rt_uint8_t timer_mask = 0;

	/* Enable Power Clock */

	/* initialize timer mask */
	timer_mask = 1UL << PM_SLEEP_MODE_DEEP;

	/* initialize system pm module */
	rt_system_pm_init(&_ops, timer_mask, RT_NULL);

	return 0;
}
INIT_BOARD_EXPORT(drv_pm_hw_init);

/**
 * @brief This function initialize the lptime
 */
int32_t drv_pm_lptim_init(void)	
{
	return bsp_lptim_init(e_bsp_lptim1, drv_pm_lptim_callback);
}
INIT_DEVICE_EXPORT(drv_pm_lptim_init);

#endif
